Skip to content

02 路由与JSON响应

上一篇文章中,我们用@app.route("/chat")定义了一个最简单的路由。但真正的Agent API不可能只有一个接口——你可能需要查询对话历史、管理会话、上传文件、获取Agent状态等等。

这篇文章就来深入学习Flask的路由系统和JSON响应,让你能设计出一套规范的Agent API。

一、路由基础

路由就是URL路径和处理函数的映射关系。用户访问某个URL,Flask就调用对应的函数。

python
@app.route("/chat")
def chat():
    return "chat接口"

用户访问/chat,Flask就执行chat()函数。

1.1 多个路由

一个应用可以有多个路由,每个URL对应不同的功能:

python
@app.route("/")
def index():
    return "首页"

@app.route("/chat")
def chat():
    return "对话接口"

@app.route("/health")
def health():
    return "健康检查"

1.2 路由末尾的斜杠

这两个路由是不一样的:

python
@app.route("/projects")
def projects():
    return "没有斜杠"

@app.route("/about/")
def about():
    return "有斜杠"

规则很简单:

写法访问/about访问/about/
@app.route("/about")正常响应404
@app.route("/about/")自动重定向到/about/正常响应

建议:统一用一种风格,避免混乱。API接口一般不加斜杠。

二、动态路由

有时候URL的一部分是变化的。比如你想通过URL直接查询某个会话的历史:

python
@app.route("/session/<session_id>")
def get_session(session_id):
    # session_id会自动从URL中提取出来
    return {"session_id": session_id}

访问/session/abc123session_id的值就是"abc123"。Flask会自动把URL中的变量部分提取出来,作为参数传给视图函数。

2.1 类型转换器

默认情况下,URL变量是字符串。你可以用转换器指定类型:

python
@app.route("/user/<username>")
def show_user(username):
    # username是字符串
    return {"username": username}

@app.route("/post/<int:post_id>")
def show_post(post_id):
    # post_id是整数
    return {"post_id": post_id}

@app.route("/price/<float:amount>")
def show_price(amount):
    # amount是浮点数
    return {"amount": amount}

内置的转换器:

转换器说明示例
string字符串(默认),不含斜杠/user/john
int正整数/post/42
float正浮点数/price/9.99
path字符串,可以含斜杠/file/a/b/c.txt
uuidUUID字符串/task/550e8400-e29b-41d4-a716-446655440000

2.2 Agent场景示例

动态路由在Agent API中很常用:

python
@app.route("/session/<session_id>/history")
def get_history(session_id):
    """获取某个会话的对话历史"""
    # 后面会从数据库或记忆中查询
    return {"session_id": session_id, "messages": []}

@app.route("/agent/<agent_name>/invoke", methods=["POST"])
def invoke_agent(agent_name):
    """调用指定的Agent"""
    data = request.get_json()
    return {"agent": agent_name, "result": "处理完成"}

三、HTTP方法

默认情况下,路由只响应GET请求。但Agent API通常需要POST请求(因为要发送用户消息)。

3.1 指定方法

python
@app.route("/chat", methods=["GET", "POST"])
def chat():
    if request.method == "POST":
        # 处理POST请求:接收用户消息
        data = request.get_json()
        return {"reply": f"收到: {data.get('message', '')}"}
    else:
        # 处理GET请求:返回使用说明
        return {"usage": "POST /chat with {message: '...'}"}

3.2 更简洁的写法

Flask提供了按方法名拆分的快捷装饰器,不用在一个函数里写if/else

python
@app.get("/health")
def health_check():
    """GET请求:健康检查"""
    return {"status": "ok"}

@app.post("/chat")
def chat():
    """POST请求:发送消息"""
    data = request.get_json()
    return {"reply": f"收到: {data.get('message', '')}"}

@app.delete("/session/<session_id>")
def delete_session(session_id):
    """DELETE请求:删除会话"""
    return {"deleted": session_id}

这种方式更清晰——每个HTTP方法对应一个函数,职责单一。

3.3 常用HTTP方法

方法用途Agent API示例
GET查询数据获取对话历史、查询Agent状态
POST创建/提交数据发送消息、创建新会话
PUT更新数据更新会话配置
DELETE删除数据删除会话

四、获取请求数据

Agent API需要从请求中获取数据。Flask的request对象提供了多种方式。

4.1 JSON请求体

这是Agent API最常用的方式——客户端发送JSON格式的数据:

python
from flask import request

@app.post("/chat")
def chat():
    data = request.get_json()
    message = data.get("message", "")
    model = data.get("model", "deepseek-v4-flash")
    return {"reply": f"使用{model}回复: {message}"}

客户端发送:

bash
curl -X POST http://localhost:5000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "你好", "model": "deepseek-v4-flash"}'

4.2 URL查询参数

问号后面的参数,适合GET请求:

python
@app.get("/history")
def history():
    page = request.args.get("page", 1, type=int)
    size = request.args.get("size", 10, type=int)
    return {"page": page, "size": size}

访问/history?page=2&size=20page就是2,size就是20。

request.args.get()的第三个参数type=int会自动把字符串转成整数,转换失败返回默认值。比手动int()转换更安全。

4.3 表单数据

传统HTML表单提交的数据:

python
@app.post("/login")
def login():
    username = request.form.get("username")
    password = request.form.get("password")
    return {"username": username}

Agent API一般用JSON,这种方式用得比较少。

4.4 请求头

有时候需要从请求头中获取信息,比如认证Token:

python
@app.post("/chat")
def chat():
    token = request.headers.get("Authorization", "")
    if not token.startswith("Bearer "):
        return {"error": "未授权"}, 401

    # 提取token
    api_key = token.replace("Bearer ", "")
    data = request.get_json()
    return {"reply": f"收到: {data.get('message', '')}"}

4.5 获取方式汇总

数据位置获取方式示例
JSON请求体request.get_json(){"message": "你好"}
URL参数request.args.get("key")/chat?model=deepseek
表单数据request.form.get("key")username=xxx
请求头request.headers.get("X-Token")Authorization: Bearer xxx
URL变量函数参数/session/<id>

五、返回JSON响应

Agent API的响应基本都是JSON格式。Flask让这件事变得非常简单。

5.1 直接返回字典

最简单的方式——直接return一个字典或列表,Flask自动转成JSON:

python
@app.get("/health")
def health():
    return {"status": "ok", "version": "1.0"}

Flask自动帮你做了:

  • 把字典序列化成JSON字符串
  • 设置Content-Type: application/json响应头
  • 返回200状态码

5.2 返回列表

也可以直接返回列表:

python
@app.get("/models")
def list_models():
    return ["deepseek-v4-flash", "deepseek-v3", "gpt-4o"]

5.3 自定义状态码

默认返回200。如果需要返回其他状态码,用元组:

python
@app.post("/chat")
def chat():
    data = request.get_json()
    if not data or "message" not in data:
        return {"error": "缺少message参数"}, 400  # 400 Bad Request

    return {"reply": "收到"}, 201  # 201 Created

元组的格式是(响应体, 状态码)

5.4 自定义响应头

需要加额外的响应头,用三元素元组:

python
@app.get("/data")
def get_data():
    return (
        {"data": [1, 2, 3]},
        200,
        {"X-Request-Id": "abc123", "Cache-Control": "no-cache"},
    )

5.5 jsonify函数

如果你需要更精细的控制,可以用jsonify

python
from flask import jsonify

@app.get("/user")
def get_user():
    return jsonify(
        username="张三",
        role="admin",
    )

jsonify会自动把关键字参数转成JSON对象,同时设置正确的Content-Type。

5.6 返回值类型汇总

返回值Flask的处理
dictlist自动转JSON,200状态码
string返回HTML,200状态码
(dict, 状态码)自动转JSON,自定义状态码
(dict, 状态码, 响应头)自动转JSON,自定义状态码和响应头
Response对象直接返回,完全自定义

六、重定向和错误

6.1 重定向

把用户引导到另一个URL:

python
from flask import redirect, url_for

@app.route("/")
def index():
    return redirect(url_for("health"))  # 重定向到 /health

@app.route("/health")
def health():
    return {"status": "ok"}

url_for("health")会根据函数名health自动生成对应的URL/health。用url_for而不是硬编码URL的好处是:改了路由规则,所有引用处自动更新。

6.2 主动中断请求

有些情况下需要主动返回错误,比如参数不合法:

python
from flask import abort

@app.post("/chat")
def chat():
    data = request.get_json()
    if not data:
        abort(400, description="请求体不能为空")

    message = data.get("message")
    if not message:
        abort(400, description="缺少message字段")

    return {"reply": f"收到: {message}"}

abort(400)会立即停止执行,返回400错误。

6.3 自定义错误响应

默认的错误页面是HTML格式的,对API不友好。你可以自定义错误处理,让错误也返回JSON:

python
from flask import jsonify

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "接口不存在"}), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({"error": "服务器内部错误"}), 500

@app.errorhandler(400)
def bad_request(error):
    return jsonify({"error": str(error.description)}), 400

这样所有错误都会返回JSON格式,前端处理起来更方便。

七、完整的Agent API骨架

把上面学到的串起来,一个结构清晰的Agent API:

python
from flask import Flask, request, jsonify, abort

app = Flask(__name__)


# ---- 健康检查 ----

@app.get("/health")
def health():
    return {"status": "ok"}


# ---- 对话接口 ----

@app.post("/chat")
def chat():
    data = request.get_json()
    if not data or "message" not in data:
        abort(400, description="缺少message字段")

    message = data["message"]
    session_id = data.get("session_id", "default")

    # 这里后面会替换成真正的Agent调用
    return {
        "reply": f"收到: {message}",
        "session_id": session_id,
    }


# ---- 会话管理 ----

@app.get("/session/<session_id>")
def get_session(session_id):
    return {"session_id": session_id, "messages": []}


@app.delete("/session/<session_id>")
def delete_session(session_id):
    return {"deleted": session_id}


# ---- 错误处理 ----

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "接口不存在"}), 404

@app.errorhandler(400)
def bad_request(error):
    return jsonify({"error": str(error.description)}), 400


if __name__ == "__main__":
    app.run(debug=True)

接口列表:

方法路径功能
GET/health健康检查
POST/chat发送消息
GET/session/<id>查询会话
DELETE/session/<id>删除会话

这就是一个Agent API的基本骨架了。后面加上Agent逻辑、数据库、流式输出,就能变成一个完整的生产级服务。

八、总结

路由和JSON响应是Agent API的基础:

  • 路由:用@app.route()@app.get()/@app.post()把URL绑定到函数
  • 动态路由<变量名>从URL提取参数,支持类型转换
  • 请求数据request.get_json()取JSON,request.args.get()取URL参数
  • JSON响应:直接return字典,Flask自动转JSON
  • 错误处理abort()中断请求,@app.errorhandler()自定义错误格式

在下一篇文章中,我们将学习Blueprint蓝图——当你的Agent API接口越来越多时,用蓝图把它们按功能拆分到不同文件中,让项目保持整洁。